<?

class Smutty_Router {

	var $controller,
			$action;

	var $defController,
		$defAction;

	/**
	 *  contructor.
	 *
	 */

	function Smutty_Router() {

		// work out default route args
		$cfg = Smutty_Config::getInstance();
		$this->defController = 'Index';
		$this->defAction = 'index';
		if ( $default = $cfg->get('route.default') ) {
			$defs = explode( '/', $default );
			if ( $defs[0] ) $this->defController = ucfirst($defs[0]);
			if ( $defs[1] ) $this->defAction = $defs[1];
		}

		// if we have a controller then we can try and do
		// some routing for it, otherwise we'll just go
		// with the default values.
		$url = explode(  '/', Smutty_Utils::getSmuttyUrl() );
		if ( Smutty_Router::isValidControllerName($url[0]) ) {
			$this->controller = ucfirst( $url[0] );
			$this->processRoute(
				$this->getRouteSpec($this->controller,$url[1]), $url
			);
		}
		else $this->controller = $this->defController;

	}

	/**
	 *  this function process the route definition and sets
	 *  the values accordingly.  the name "action" is
	 *  special and changes the current value.
	 *
	 *  @param [spec] spec string
	 *  @param [url] array of url parts
	 *
	 */

	function processRoute( $spec, $url ) {

		$parts = explode( '/', $spec );
		$data =& Smutty_Data::getInstance();

		for ( $i=0; $i<sizeof($parts); $i++ )
			switch ( $parts[$i] ) {
				case 'action':
					// only override if the value is set, otherwise
					// we just want to use the default
					if ( $url[$i] )
						$this->action = $url[$i];
					break;
				default:
					$data->set( $parts[$i], urldecode($url[$i]) );
			}

	}

	/**
	 *  this function searches the application config trying
	 *  to match a route.  if none is found then the default
	 *  route will be returned.  you can specify a controller
	 *  and action to find routes bound to them.
	 *
	 *  @param [ctl] optional controller name
	 *  @param [act] optional action name
	 *
	 */

	function getRouteSpec( $ctl = false, $act = false ) {

		$cfg = Smutty_Config::getInstance();
		$ctl = strtolower( $ctl );

		// if we're using the smutty controller then we need to
		// define our own routes here
		if ( $ctl == 'smutty' )
			switch ( $act ) {
				case 'resource':
					return 'controller/action/folder/file';
					break;
			}

		// check for action match
		elseif ( $spec = $cfg->get("route.$ctl.$act") )
			return "controller/action/$spec";

		// check for controller match
		elseif ( $spec = $cfg->get("route.$ctl") )
			return "controller/$spec";

		// nothing matched, return default spec
		else return 'controller/action/id';

	}

	/**
	 *  tests if a given controller name is valid
	 *
	 *  @param [name] the name to test
	 *  @return boolean indicating validity
	 *
	 */

	function isValidControllerName( $name ) {
		// remove valid chars, if there's anything left
		// then it's not valid
		$badChars = preg_replace( '/[A-Z]/i', '', $name );
		return ( $name && !$badChars );
	}

	/**
	 *  returns the name of the default controller
	 *
	 *  @return String
	 *
	 */

	function getDefaultControllerName() {
		return $this->defController;
	}

	/**
	 *  returns the instance of this class
	 *
	 *  @return Smutty_Router
	 *
	 */

	function getInstance() {
		static $instance;
		if ( $instance == null )
			$instance = new Smutty_Router();
		return $instance;
	}

	/**
	 *  returns the name of the current controller
	 *
	 *  @return String
	 *
	 */

	function getControllerName() {
		return $this->controller;
	}

	/**
	 *  returns the current action
	 *
	 *  @return String
	 *
	 */

	function getActionName() {
		return $this->action
			? $this->action : $this->defAction;
	}

	/**
	 *  dispatches the request
	 *
	 */

	function dispatch() {

		// first need to include controller class
		if ( strtolower($this->controller) == 'smutty' )
			Smutty_Main::loadClass( 'Smutty_Controller_Smutty' );
		else {
			$controllerFile = 'application/controllers/' . $this->controller . 'Controller.php';
			if ( !file_exists($controllerFile) )
				new Smutty_Exception( ERR_CONTROLLER_INVALID, 'ClassSmutty_Controller' );
			require $controllerFile;
		}

		// now instantiate controller
		if ( strtolower($this->controller) == 'smutty' )
			$controller = new Smutty_Controller_Smutty();
		else {
			$controllerClass = $this->controller . 'Controller';
			$controller = new $controllerClass();
		}

		// make sure the method exists
		$action = $this->getActionName() . 'Action';
		if ( !method_exists($controller,$action) )
			new Smutty_Exception( ERR_ACTION_INVALID, 'ClassSmutty_Controller' );
		// if we have the reflection classes we can check
		// for visibility modifiers
		if ( class_exists('ReflectionClass') ) {
			$refl = new ReflectionClass( $controllerClass );
			$meth = $refl->getMethod( $action );
			if ( !$meth->isPublic() )
				new Smutty_Exception( ERR_ACTION_INVALID, 'ClassSmutty_Controller' );
		}

		// we're ready to call the action, so get the data
		// that we're gonna pass to it and go go go!
		$session =& Smutty_Session::getInstance();
		$data = Smutty_Data::getInstance();
		$controller->$action(
			$data,
			$session
		);

	}

}

?>